#include "esp_timer.h"
#include "esp_system.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "iot_usbh_rndis.h"
#include "iot_eth.h"
#include "iot_eth_netif_glue.h"
#include "iot_usbh_cdc.h"
#include "at_command_handler.h"
#include "usbh_modem_board.h"
#include "modem_rndis.h"

static const char *TAG = "rndis_modem";

static at_command_handler_t s_at_handler = NULL;

esp_err_t send_at_command(const char* command, char* response, size_t response_len, uint32_t timeout_ms)
{
    if (s_at_handler == NULL) {
        ESP_LOGW(TAG, "AT command interface not initialized, skipping command: %s", command);
        return ESP_ERR_INVALID_STATE;
    }

    return at_command_send(s_at_handler, command, response, response_len, timeout_ms);
}

static esp_err_t check_sim_card_status(void)
{
    esp_err_t ret = ESP_FAIL;
    int retries = 30;
    char response[512] = {0};
    
    ESP_LOGI(TAG, "Starting SIM card status check...");
    
    while (retries-- > 0) {
        ret = send_at_command("AT+CPIN?\r", response, sizeof(response), 2000);
        if (ret != ESP_OK) {
            ESP_LOGW(TAG, "Failed to send AT+CPIN? command, retrying...");
            continue;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
        if (strstr(response, "+CPIN: READY") != NULL) {
            ESP_LOGI(TAG, "SIM card is ready");
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_SIMCARD_CONN, NULL, 0, 0);
            return ESP_OK;
        } else if (strstr(response, "+CME ERROR: 100") != NULL) {
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_SIMCARD_DISCONN, NULL, 0, 0);
        }        
        ESP_LOGI(TAG, "Waiting for SIM card to be ready... (%d retries left)", retries);
    }
    
    ESP_LOGE(TAG, "SIM card not ready after all retries!");
    return ESP_FAIL;
}

static esp_err_t rndis_dial_up(void) 
{
    ESP_LOGI(TAG, "Checking SIM card status...");
    esp_err_t ret = check_sim_card_status();
#ifdef CONFIG_MODEM_TARGET_CT511
    return ret;
#else
    if (ret == ESP_OK) {
        vTaskDelay(pdMS_TO_TICKS(3000));
        char dialup_response[200] = {0};
       ret = send_at_command("AT+MDIALUP=1,1\r", dialup_response, sizeof(dialup_response), 5000);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "Failed to send dialup command");
        } else {
            ESP_LOGI(TAG, "Dialup command sent successfully");
            ESP_LOGI(TAG, "Response: %s", dialup_response);
            if (strstr(dialup_response, "+MDIALUP: 1,1") != NULL) {
                ESP_LOGI(TAG, "Dialup successfully");
                esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_NET_CONN, NULL, 0, 0);
            } else {
                esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_NET_DISCONN, NULL, 0, 0);
            }
        }
        return ret;
    } else {
        ESP_LOGE(TAG, "SIM card not ready, dialup command not sent");
    }
    return ESP_FAIL;
#endif
}

static esp_err_t init_at_command_interface_with_retry(void)
{
    at_command_config_t at_config = AT_COMMAND_DEFAULT_CONFIG();
#ifdef CONFIG_MODEM_SUPPORT_SECONDARY_AT_PORT
    at_config.itf_num = CONFIG_MODEM_USB_ITF2;
#else
    at_config.itf_num = CONFIG_MODEM_USB_ITF;
#endif
    at_config.rx_buffer_size = 1024;
    at_config.tx_buffer_size = 1024;
    at_config.default_timeout_ms = 5000;
    at_config.max_retry_count = 3;

    esp_err_t ret = at_command_create(&at_config, &s_at_handler);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "AT command handler created successfully.");
        return ret;
    }
    
    ESP_LOGE(TAG, "Failed to create AT command handler: %s", esp_err_to_name(ret));
    return ret;
}

static esp_err_t init_at_command_interface(void)
{
    esp_err_t ret = init_at_command_interface_with_retry();
    if (ret != ESP_OK) {
        ESP_LOGW(TAG, "AT command interface not available, continuing without it");
        s_at_handler = NULL;
        return ESP_OK; 
    }

    ESP_LOGI(TAG, "AT command interface initialized successfully");
    return ESP_OK;
}

esp_err_t modem_rndis_get_signal_quality(int *rssi, int *ber)
{
    char csq_response[200] = {0};
    esp_err_t ret = send_at_command("AT+CSQ\r", csq_response, sizeof(csq_response), 1000); 
    if (ret == ESP_OK) {           
        char *csq_start = strstr(csq_response, "+CSQ:");
        if (csq_start) {
            if (sscanf(csq_start, "+CSQ: %d,%d", rssi, ber) == 2) {
                return ESP_OK;
            }
        }
    } else {
        ESP_LOGE(TAG, "Failed to send AT+CSQ command");
    }
    return ret;
}

static void iot_event_handle(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    switch (event_id) {
        case IOT_ETH_EVENT_START:
            ESP_LOGI(TAG, "IOT_ETH_EVENT_START");
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_ETH_START, NULL, 0, 0);
            break;
        case IOT_ETH_EVENT_STOP:
            ESP_LOGI(TAG, "IOT_ETH_EVENT_STOP");
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_ETH_STOP, NULL, 0, 0);
            break;
        case IOT_ETH_EVENT_CONNECTED:
            ESP_LOGI(TAG, "IOT_ETH_EVENT_CONNECTED");
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_ETH_CONNECTED, NULL, 0, 0);
            rndis_dial_up();
            break;
        case IOT_ETH_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "IOT_ETH_EVENT_DISCONNECTED");
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_ETH_DISCONNECTED, NULL, 0, 0);
            esp_event_post(MODEM_BOARD_EVENT, MODEM_EVENT_NET_DISCONN, NULL, 0, 0);
            break;
        default:
            ESP_LOGI(TAG, "IOT_ETH_EVENT_UNKNOWN");
            break;
    }
}

esp_err_t modem_rndis_init(rndis_config_t *config)
{ 
    /* Start RNDIS driver */
    esp_event_handler_register(IOT_ETH_EVENT, ESP_EVENT_ANY_ID, iot_event_handle, NULL);
    esp_event_handler_register(MODEM_BOARD_EVENT, ESP_EVENT_ANY_ID, config->handler, NULL);

    // install usbh cdc driver
    usbh_cdc_driver_config_t cdc_config = {
        .task_stack_size = config->task_stack_size,
        .task_priority = config->task_priority,
        .task_coreid = config->task_coreid,
        .skip_init_usb_host_driver = config->skip_init_usb_host_driver,
    };
    ESP_ERROR_CHECK(usbh_cdc_driver_install(&cdc_config));

    iot_usbh_rndis_config_t rndis_cfg = {
        .auto_detect = config->auto_detect,
        .auto_detect_timeout = config->auto_detect_timeout,
    };

    iot_eth_driver_t *rndis_handle = NULL;
    esp_err_t ret = iot_eth_new_usb_rndis(&rndis_cfg, &rndis_handle);
    if (ret != ESP_OK || rndis_handle == NULL) {
        ESP_LOGE(TAG, "Failed to create USB RNDIS driver");
    }

    iot_eth_config_t eth_cfg = {
        .driver = rndis_handle,
        .stack_input = NULL,
        .user_data = NULL,
    };

    iot_eth_handle_t eth_handle = NULL;
    ret = iot_eth_install(&eth_cfg, &eth_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to install USB RNDIS driver");
        return ret;
    }

    esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
    esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);

    iot_eth_netif_glue_handle_t glue = iot_eth_new_netif_glue(eth_handle);
    if (glue == NULL) {
        ESP_LOGE(TAG, "Failed to create netif glue");
        return ESP_FAIL;
    }
    esp_netif_attach(eth_netif, glue);

    while (1) {
        ret = iot_eth_start(eth_handle);
        if (ret != ESP_OK) {
            ESP_LOGW(TAG, "Failed to start USB RNDIS driver, try again...");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }
        break;
    }
    
    ret = init_at_command_interface();
    if (ret != ESP_OK) {
        ESP_LOGW(TAG, "Failed to initialize AT command interface");
    }

    return ESP_OK;
}

